home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / util / cd_link.c < prev    next >
C/C++ Source or Header  |  1993-01-08  |  15KB  |  642 lines

  1. /* Unix(TM) CD-ROM symbolic link utility  by
  2.         Matthew B. Hornbeck, Director of Technical Services
  3.         Copyright 1989, 1990, 1991 by Young Minds, Incorporated
  4.         June 6, 1991.
  5. File : CD_LINK.C
  6.  
  7. Note : On HP-UX machines, you must link this program using the BSD
  8.     compatible library (i.e. "cc -o cd_link cd_link.c /usr/lib/libBSD.a")
  9. */
  10.  
  11. #define TRANSLATION_FILE    "00_TRANS.TBL;1"
  12. #define RRIP_TRANSLATION_FILE    "00_TRANS.TBL"
  13.  
  14. #ifdef sun
  15. #define REALPATH
  16. #endif
  17.  
  18. #include <stdio.h>
  19. #include <string.h>
  20. #include <ctype.h>
  21. #include <malloc.h>
  22. #include <sys/types.h>
  23. #include <sys/dir.h>
  24. #include <sys/stat.h>
  25.  
  26. #ifdef REALPATH
  27. #include <sys/param.h>
  28. #else
  29. #define MAXPATHLEN    4096
  30. #endif
  31.  
  32. #define MAX_TRANS_TYPES    (4)
  33. #ifndef FALSE
  34. #define FALSE    (0)
  35. #define TRUE    (!FALSE)
  36. #endif
  37.  
  38. /*
  39. **    Bit masks for setting flags bit-vector from
  40. **    command line args.
  41. */
  42. #define RECURSE        (1)
  43. #define ROCK_RIDGE    (2)
  44.  
  45. char*
  46. get_program_name( argv0 )
  47.     char    *argv0;
  48. {
  49.     char    *program_name;
  50.  
  51.     /*
  52.     **    Gets the component of the command that
  53.     **    occurs after the last '/'.
  54.     */
  55.     program_name = strrchr( argv0, '/' );
  56.     if (program_name == NULL)
  57.         program_name = argv0;
  58.     else
  59.         program_name++;
  60.     return( program_name );
  61. }
  62.  
  63. void
  64. usage( program_name, arg_list, error_message)
  65.     char    *program_name;
  66.     char    *arg_list;
  67.     char    *error_message;
  68. {
  69.     fprintf( stderr, "Usage: %s %s\n", program_name, arg_list );
  70.     fprintf( stderr, "\t%s\n", error_message );
  71. }
  72.  
  73. typedef struct dir_element {
  74.     struct dir_element *next;
  75.     char *cd_path;
  76.     char *new_path;
  77. } dir_elem;
  78.  
  79. static dir_elem *head = NULL, *tail = NULL;
  80.  
  81. int 
  82. push_dir (cd_path, new_path)
  83.     char *cd_path, *new_path;
  84.  
  85. {
  86.     dir_elem *tmp;
  87.  
  88.     if (head == NULL)
  89.       {    if ((head = tmp = (dir_elem *) malloc (sizeof (dir_elem))) == NULL)
  90.           {    fprintf (stderr, "Unable to allocate dir_element buffer!\n");
  91.             return FALSE;
  92.           }
  93.       }
  94.     else
  95.       {    if ((tail->next = tmp = (dir_elem *) malloc (sizeof (dir_elem))) == NULL)
  96.           {    fprintf (stderr, "Unable to allocate dir_element buffer!\n");
  97.             return FALSE;
  98.           }
  99.       }
  100.     tmp->next = NULL;
  101.     if ((tmp->cd_path = (char *) malloc (strlen (cd_path) + 1)) == NULL)
  102.       {    fprintf (stderr, "Unable to allocate cd_path of dir_element!\n");
  103.         return FALSE;
  104.       }
  105.     strcpy (tmp->cd_path, cd_path);
  106.     if ((tmp->new_path = (char *) malloc (strlen (new_path) + 1)) == NULL)
  107.       {    fprintf (stderr, "Unable to allocate new_path of dir_element!\n");
  108.         return FALSE;
  109.       }
  110.     strcpy (tmp->new_path, new_path);
  111.     tail = tmp;
  112.     return TRUE;
  113. }
  114.  
  115. dir_elem *
  116. dequeue_dir()
  117.  
  118. {    
  119.     dir_elem *tmp;
  120.  
  121.     if (head == NULL)
  122.       {    tail = NULL;
  123.         return NULL;
  124.       }
  125.     tmp = head;
  126.     head = tmp->next;
  127.     tmp->next = NULL;
  128.     return tmp;
  129. }
  130.  
  131. void
  132. free_dir (dir)
  133.     dir_elem *dir;
  134.  
  135. {
  136.     free (dir->cd_path);
  137.     free (dir->new_path);
  138.     free (dir);
  139. }
  140.  
  141. void
  142. translate_name( name, trans_type)
  143.     char    *name;
  144.     int        trans_type;
  145. {
  146.     int    i;
  147.  
  148.     /*
  149.     **    Changes the name given according to one of the algorithms
  150.     **    below.  The algorithm used is selected via the trans_type
  151.     **    arguement to this function.
  152.     */
  153.     switch( trans_type ) {
  154.         case 0:
  155.             /*
  156.             **    No translation.  Use original name.
  157.             */
  158.             break;
  159.  
  160.         case 1:
  161.             /*
  162.             **    All lower case.
  163.             */
  164.             for (i = 0; i < strlen(name) ; i ++) {
  165.                 if (isupper (name [i]))
  166.                     name [i] = tolower (name [i]);
  167.                 else
  168.                     name [i] = name [i];
  169.             }
  170.             break;
  171.         case 2:
  172.             /*
  173.             **    All lower case.  Strip ";version_no".
  174.             */
  175.             for (i = 0; (name[i] != ';') && (i < strlen( name )); i ++) {
  176.                 if (isupper (name [i]))
  177.                     name [i] = tolower (name [i]);
  178.                 else 
  179.                     name [i] = name [i];
  180.             }
  181.             name[i] = '\0';
  182.             break;
  183.         case 3:
  184.             /*
  185.             **    All lower case.  Replace ";version_no" with "-version_no".
  186.             */
  187.             for (i = 0; i < strlen( name ); i ++) {
  188.                 if ( name[i] == ';' )
  189.                     name[i] = '-';
  190.                 else if (isupper (name [i]))
  191.                     name [i] = tolower (name [i]);
  192.                 else 
  193.                     name [i] = name [i];
  194.             }
  195.             name[i] = '\0';
  196.             break;
  197.         default:
  198.             fprintf(stderr, "translate_name: Unknown translation type.\n");
  199.             exit( 1 );
  200.     }
  201. }
  202.  
  203. FILE*
  204. open_trans( cd_path, trans_name, trans_type )
  205.     char    *cd_path;
  206.     char    *trans_name;
  207.     int        *trans_type;
  208. {
  209.     FILE    *fp;
  210.     char    *new_name;
  211.     char    *name;
  212.     int        i;
  213.  
  214.     /*
  215.     **    Get space for resolved file name which consistes of the cd_path
  216.     **    and the translated trans_name (new_name) concatinated together.
  217.     */
  218.     if ((name = malloc( strlen( trans_name ) + strlen(cd_path) + 2 )) == NULL) {
  219.         fprintf(stderr, "Error: Malloc failed.\n");
  220.         exit( 1 );
  221.     }
  222.     /*
  223.     **    Get space to put the translated trans_name.
  224.     */
  225.     if ((new_name = malloc( strlen( trans_name ) + 1 )) == NULL) {
  226.         fprintf(stderr, "Error: Malloc failed.\n");
  227.         exit( 1 );
  228.     }
  229.     /*
  230.     **    translate the trans_name using the translation type
  231.     **    that was previously found, or if first time translation
  232.     **    type defaults to 0.
  233.     */
  234.     strcpy( new_name, trans_name );
  235.     translate_name( new_name, *trans_type );
  236.     /*
  237.     **    Concatinate translated name and cd_path to get resolved name.
  238.     */
  239.     sprintf( name, "%s/%s", cd_path, new_name );
  240.     /*
  241.     **    Attempt to open the file.
  242.     **    If fopen fails then I will try some other translation types on
  243.     **    on the trans_name.
  244.     */
  245.     if ((fp = fopen (name, "rt")) != NULL) {
  246.         free( new_name );
  247.         free( name );
  248.         return( fp );
  249.     }
  250.     /*
  251.     **    Try translation types on trans_name until I can either
  252.     **    open the file successfully or I run out of translation
  253.     **    types.
  254.     */
  255.     for (i = 0; i < MAX_TRANS_TYPES; i++) {
  256.         strcpy( new_name, trans_name );
  257.         translate_name( new_name, i );
  258.         sprintf( name, "%s/%s", cd_path, new_name );
  259.         if ((fp = fopen (name, "rt")) != NULL) {
  260.             *trans_type = i;
  261.             free( new_name );
  262.             free( name );
  263.             return( fp );
  264.         }
  265.     }
  266.     /*
  267.     **    Failed to open the file.
  268.     **    Return NULL file descriptor to signal error.
  269.     */
  270.     free( new_name );
  271.     free( name );
  272.     return( NULL );
  273. }
  274.  
  275. int
  276. rrip_proc_dir( cd_path, new_path, recurse )
  277.     char    *cd_path;
  278.     char    *new_path;
  279.     int        recurse;
  280. {
  281.     FILE            *fp;
  282.     char            line[MAXPATHLEN];
  283.     char            file_name[MAXPATHLEN];
  284.     char            link_buf[MAXPATHLEN];
  285.     char            trans_name[MAXPATHLEN];
  286.     char            link_name [MAXPATHLEN];
  287.     char            new_name [MAXPATHLEN];
  288.     char            resolved_name [MAXPATHLEN];
  289.     char            type;
  290.     int                num_fields;
  291.  
  292.     /*
  293.     **    For each directory entry.  Get its type.  
  294.     **    Depending on its type make a directory or symbolic link.
  295.     **    If the type is a directory and directory recursion was
  296.     **    asked for on the command line, then push it onto the
  297.     **    stack to be proccessed later.
  298.     */
  299.     sprintf( trans_name, "%s/%s", cd_path, RRIP_TRANSLATION_FILE );
  300.     if ((fp = fopen( trans_name, "rt" )) == NULL ) {
  301.         fprintf (stderr, "Unable to open translation file %s!\n", trans_name);
  302.         return FALSE;
  303.     }
  304.     while (fgets( line, sizeof( line ), fp) != NULL) {
  305.         /*
  306.         **    Get the type of the file,
  307.         **    the file name and the link name if this entry is a link.
  308.         */
  309.         strcpy( link_name, "" );
  310.         num_fields = sscanf( line, "%c %*s %s %s", 
  311.                                             &type, file_name, link_name );
  312.  
  313.         if (strcmp( file_name, ".") == 0)
  314.             continue;
  315.         if (strcmp( file_name, "..") == 0)
  316.             continue;
  317.         sprintf (new_name, "%s/%s", new_path, file_name);
  318.         switch (type) {
  319.             case 'F' :    
  320.                 sprintf (link_name, "%s/%s", cd_path, file_name);
  321.                 if (symlink (link_name, new_name) != 0)
  322.                     fprintf (stderr, "Unable to make link %s to %s!\n", 
  323.                                                 link_name, new_name);
  324.                     break;
  325.  
  326.             case 'L' :    
  327.                 if (symlink (link_name, new_name) != 0)
  328.                     fprintf (stderr, "Unable to make link %s to %s!\n", 
  329.                                                 link_name, new_name);
  330.                     break;
  331.  
  332.             case 'D' :    
  333.                 mkdir (new_name, 0777);
  334.                 if (recurse) {
  335.                     sprintf (link_name, "%s/%s", cd_path, file_name);
  336.                     push_dir (link_name, new_name);
  337.                 }
  338.                 break;
  339.  
  340.             case 'M' :    
  341.                 mkdir (new_name, 0777);
  342.                 if (recurse) {    
  343.                     sprintf (link_buf, "%s/%s", cd_path, link_name);
  344. #ifdef REALPATH
  345.                     realpath (link_buf, resolved_name);
  346. #else
  347.                     strcpy (resolved_name, link_buf);
  348. #endif
  349.                     push_dir (resolved_name, new_name);
  350.                 }
  351.                 break;
  352.             default:
  353.                 fprintf(stderr, "proc_dir:    Unknown file type.\n");
  354.                 exit( 1 );
  355.         }
  356.     }
  357.     fclose (fp);
  358.     return TRUE;
  359. }
  360.  
  361. int
  362. iso9660_proc_dir( cd_path, new_path, recurse )
  363.     char    *cd_path;
  364.     char    *new_path;
  365.     int        recurse;
  366. {
  367.     FILE            *fp;
  368.     char            line [4096], link_name [4096], new_name [4096];
  369.     char            trans_name [4096], resolved_name [MAXPATHLEN];
  370.     char            type, elem1 [65], elem2 [2048], elem3 [2048];
  371.     int             line_cnt, elem_cnt, i, j;
  372.     static int        trans_type = 0;
  373.  
  374.     sprintf (trans_name, "%s/%s", cd_path, TRANSLATION_FILE);
  375.     if ((fp = open_trans( cd_path, TRANSLATION_FILE, &trans_type )) == NULL)
  376.       {    
  377.         fprintf (stderr, "Unable to open file %s!\n", trans_name);
  378.         return FALSE;
  379.       }
  380.     line_cnt = 0;
  381.     while (fgets (line, sizeof (line), fp) != NULL) {
  382.         line_cnt ++;
  383.         if ((strlen (line) < 19) || (line [1] != ' ') || (line [strlen (line) - 1] != '\n'))
  384.           {    fprintf (stderr, "Invalid %s file!?!\n", trans_name);
  385.             exit (1);
  386.           }
  387.         type = line [0];
  388.  
  389.         /*
  390.         **    Get the ISO name.
  391.         */
  392.         for (i = 2, j = 0; (line [i] != ' ') && (line [i] != '\t'); i ++, j ++)
  393.             elem1 [j] = line [i];
  394.  
  395.         elem1 [j] = '\0';
  396.  
  397.         /*
  398.         **    translate name to the same format that was required
  399.         **    in order to open the "00_TRANS.TBL;1".
  400.         */
  401.         translate_name( elem1, trans_type );
  402.         /*
  403.         **    Skip past white space.
  404.         */
  405.         while ((line [i] == ' ') || (line [i] == '\t'))
  406.             i ++;
  407.  
  408.         /*
  409.         **    Get the unix name.
  410.         */
  411.         for (j = 0; (line [i] != '\t') && (line [i] != '\n'); i ++, j ++)
  412.             elem2 [j] = line [i];
  413.         elem2 [j] = '\0';
  414.  
  415.         elem_cnt = 2;
  416.         j = 0;
  417.         if (line [i] == '\t') {
  418.             /*
  419.             **    Get name of file that this name is a link to 
  420.             **    if this is a link.
  421.             */
  422.             for (i ++; line [i] != '\n'; i ++, j ++)
  423.                 elem3 [j] = line [i];
  424.             elem_cnt ++;
  425.         }
  426.         elem3 [j] = '\0';
  427.         if ((line_cnt == 1) && (strcmp (elem1, ".") == 0))
  428.             continue;
  429.         if ((line_cnt == 2) && (strcmp (elem1, "..") == 0))
  430.             continue;
  431.         sprintf (new_name, "%s/%s", new_path, elem2);
  432.         switch (type) {
  433.             case 'F' :    
  434.                 sprintf (link_name, "%s/%s", cd_path, elem1);
  435.                 if (symlink (link_name, new_name) != 0)
  436.                     fprintf (stderr, "Unable to make link %s to %s!\n", 
  437.                                                                 elem1, elem2);
  438.                     break;
  439.  
  440.             case 'L' :    
  441.                 if (symlink (elem3, new_name) != 0)
  442.                     fprintf (stderr, "Unable to make link %s to %s!\n", 
  443.                                                                 elem1, elem3);
  444.                     break;
  445.  
  446.             case 'D' :    
  447.                 mkdir (new_name, 0777);
  448.                 if (recurse) {
  449.                     sprintf (link_name, "%s/%s", cd_path, elem1);
  450.                     push_dir (link_name, new_name);
  451.                 }
  452.                 break;
  453.  
  454.             case 'M' :    
  455.                 mkdir (new_name, 0777);
  456.                 if (recurse) {    
  457.                     sprintf (link_name, "%s/%s", cd_path, elem3);
  458. #ifdef REALPATH
  459.                     realpath (link_name, resolved_name);
  460. #else
  461.                     strcpy (resolved_name, link_name);
  462. #endif
  463.                     push_dir (resolved_name, new_name);
  464.                 }
  465.                 break;
  466.             default:
  467.                 fprintf(stderr, "proc_dir:    Unknown file type.\n");
  468.                 exit( 1 );
  469.         }
  470.     }
  471.     fclose (fp);
  472.     return TRUE;
  473. }
  474.  
  475. int 
  476. proc_dir (cd_path, new_path, flags)
  477.     char *cd_path, *new_path;
  478.     int flags;
  479.  
  480. {    
  481.     int    recurse;
  482.  
  483.     /*
  484.     **    If command line arguement "-r" was specified then
  485.     **    recurse down subdirectories.
  486.     */
  487.     if ((flags & RECURSE) != 0)
  488.         recurse = TRUE;
  489.     else
  490.         recurse = FALSE;
  491.  
  492.     /*
  493.     **    If command line arguement "-R" was specified then
  494.     **    ignore the 00_TRANS.TBL and create links with the 
  495.     **    same names as the names that exist in the directory
  496.     **    entries on the disk.
  497.     **
  498.     **    This is most useful on Rock Ridge disks where the
  499.     **    name in the directory entry is the name that should
  500.     **    be used.
  501.     */
  502.     if ((flags & ROCK_RIDGE) != 0 )
  503.         rrip_proc_dir( cd_path, new_path, recurse );
  504.     else
  505.         iso9660_proc_dir( cd_path, new_path, recurse );
  506. }
  507.  
  508. int main (argc, argv)
  509. int argc;
  510. char *argv [];
  511.  
  512. {    dir_elem    *cur_dir;
  513.     char        cd_pathname [2048];
  514.     char        target_dirname[2048];
  515.     char        resolved_cd_name [MAXPATHLEN];
  516.     char        resolved_dir_name [MAXPATHLEN];
  517.     int            flags;
  518.  
  519.     int        this_arg = 1;
  520.     int        switch_count;
  521.     char    *program_name;
  522.     char    error_message[80];
  523.     char    *arg_list = "[-rR] cd_pathname [target_dir]";
  524.  
  525.     fprintf (stderr, 
  526.         "cd_link : Copyright 1989, 1990, 1991 By Young Minds, Incoporated\n");
  527.  
  528.     /*    Extract program name from first arguement.    */
  529.     program_name = get_program_name(argv[0]);
  530.  
  531.     /*    Process arguements    */
  532.     flags = 0;
  533.     cd_pathname[0] = '\0';
  534.     target_dirname[0] = '\0';
  535.     while (this_arg < argc) {
  536.         /*    Process switches    */
  537.         if (argv[this_arg][0] == '-') {
  538.             switch_count = 1;
  539.             while (argv[this_arg][switch_count] != '\0') {
  540.                 switch (argv[this_arg][switch_count]) {
  541.                     case 'r' :
  542.                         /*
  543.                         **    If command line arguement "-r" was specified then
  544.                         **    recurse down subdirectories.
  545.                         */
  546.                         flags |= RECURSE;
  547.                         break;
  548.                     case 'R' :
  549.                         /*
  550.                         **    If command line arguement "-R" was specified then
  551.                         **    ignore the 00_TRANS.TBL and create links with the 
  552.                         **    same names as the names that exist in the directory
  553.                         **    entries on the disk.
  554.                         **
  555.                         **    This is most useful on Rock Ridge disks where the
  556.                         **    name in the directory entry is the name that should
  557.                         **    be used.
  558.                         */
  559.                         flags |= ROCK_RIDGE;
  560.                         break;
  561.                     default :
  562.                         sprintf(error_message, "Unknown switch: -%c", 
  563.                                                 argv[this_arg][switch_count]);
  564.                         usage( program_name, arg_list, error_message );
  565.                         exit(1);
  566.                         break;
  567.                 }
  568.                 switch_count++;
  569.             }
  570.         }
  571.         /*    Process everything else.    */
  572.         else {
  573.             /*    Get input file name.    */
  574.             if (cd_pathname[0] != '\0') {
  575.                 if (target_dirname[0] != '\0') {
  576.                     /*    
  577.                     **    If already gotten then an error exists 
  578.                     **    in the command line.
  579.                     */
  580.                     sprintf( error_message, "Invalid arguement: %s", 
  581.                                                             argv[this_arg] );
  582.                     usage( program_name, arg_list, error_message );
  583.                     exit(1);
  584.                 }
  585.                 else
  586.                     if (argv [this_arg] [0] == '/')
  587.                         strcpy (target_dirname, argv [this_arg]);
  588.                     else {    
  589.                         getwd (target_dirname);
  590.                         strcat (target_dirname, "/");
  591.                         strcat (target_dirname, argv [this_arg]);
  592.                       }
  593.             }
  594.             else {
  595.                 if (argv [this_arg] [0] == '/')
  596.                     strcpy (cd_pathname, argv [this_arg]);
  597.                 else {    
  598.                     getwd (cd_pathname);
  599.                     strcat (cd_pathname, "/");
  600.                     strcat (cd_pathname, argv [this_arg]);
  601.                   }
  602.             }
  603.         }
  604.         this_arg++;
  605.     }
  606.  
  607.     /*
  608.     **    If there was an input file specified then use that file for
  609.     **    input.  Otherwise, get input from stdin.
  610.     */ 
  611.     if (cd_pathname[0] == '\0') {
  612.         sprintf( error_message, "Missing cd_pathname.");
  613.         usage( program_name, arg_list, error_message );
  614.         exit(1);
  615.     }
  616.  
  617.     /*
  618.     **    If there was an output file specified then use that file for
  619.     **    output.  Otherwise, put output to stdout.
  620.     */
  621.     if (target_dirname[0] == '\0') {
  622.         getwd (target_dirname);
  623.     }
  624.  
  625.  
  626. #ifdef REALPATH
  627.     realpath (cd_pathname, resolved_cd_name);
  628.     realpath (target_dirname, resolved_dir_name);
  629. #else
  630.     strcpy (resolved_cd_name, cd_pathname);
  631.     strcpy (resolved_dir_name, target_dirname);
  632. #endif
  633.  
  634.     push_dir ( resolved_cd_name, resolved_dir_name );
  635.     while ((cur_dir = dequeue_dir ()) != NULL)
  636.       {    
  637.         proc_dir (cur_dir->cd_path, cur_dir->new_path, flags);
  638.         free_dir (cur_dir);
  639.       }
  640.     return 0;
  641. }
  642.